package com.wimcorp.magic.board;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Transient;

import org.springframework.transaction.annotation.Transactional;

import com.wimcorp.magic.abilities.Ability;
import com.wimcorp.magic.cards.PlayingCard;
import com.wimcorp.magic.players.Player;

@Entity
public class PlayingDeck extends CardAccessor {

	

	@Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE)
	private Long id;

	private String name;

	// Probably will need this configuration later
	// @OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
	// @JoinTable(name = "playingdeck_playingcard", joinColumns =
	// @JoinColumn(name = "playingdeck_id"), inverseJoinColumns =
	// @JoinColumn(name = "playingcard_id"))
	// @OrderColumn
	@Transient
	private Collection<PlayingCard> deck = new LinkedList<PlayingCard>();

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "playingDeck")
	private List<PlayingDeckPlayingCard> playingDeckPlayingCards = new LinkedList<PlayingDeckPlayingCard>();

	@Transient
	private Map<Player, List<Ability>> drawObservers = new HashMap<>();
	
	protected PlayingDeck() {
	}

	public PlayingDeck(String name) {
		this.name = name;
	}

	private PlayingDeck(String name, List<PlayingCard> playingCards) {
		this.name = name;
		for (PlayingCard card : playingCards) {
			addOnTop(card);
		}
		shuffle();
	}

	public void addCards(List<PlayingDeckPlayingCard> playingCards) {
		for (PlayingDeckPlayingCard card : playingCards) {
			playingDeckPlayingCards.add(card);
		}

	}

	public List<PlayingCard> drawCards(int amount) {
		List<PlayingCard> cards = new ArrayList<PlayingCard>();
		for (int i = 1; i <= amount; i++) {
			// trigger draw abilities
			for (Player player : drawObservers.keySet()) {
				for (Ability ability : drawObservers.get(player)) {
					player.cast(ability);
				}
			}
			cards.add(getTopCard());
		}
		return cards;
	}

	/**
	 * Removes the top card from the deck
	 * 
	 * @return The top card on the deck
	 */
	public PlayingCard getTopCard() {
		return getDeck().poll();
	}

	public PlayingCard reveal() {
		return getDeck().get(0);
	}
	
	public PlayingCard reveal(int index) {
		return getDeck().get(index);
	}

	/**
	 * Adds a card to the bottom of the deck.
	 * 
	 * @param card
	 */
	public void addOnBottom(PlayingCard card) {
		getDeck().addLast(card);
	}

	public void addOnBottom(Collection<PlayingCard> cards) {
		for (PlayingCard card : cards) {
			addOnBottom(card);
		}
	}

	/**
	 * Adds a card to the top of the deck.
	 * 
	 * @param card
	 */
	public void addOnTop(PlayingCard card) {
		getDeck().addFirst(card);
	}

	public void addOnTop(Collection<PlayingCard> cards) {
		for (PlayingCard card : cards) {
			addOnTop(card);
		}
	}

	public void shuffle() {
		LinkedList<PlayingCard> deck = getDeck();
		for (int i = 0; i < deck.size(); i++) {
			int card = (int) (Math.random() * (deck.size() - i));
			deck.addLast(deck.remove(card));
		}
	}

	public LinkedList<PlayingCard> getDeck() {
		return ((LinkedList<PlayingCard>) deck);
	}

	@Transactional
	public PlayingDeck clone() {
		PlayingDeck clone = new PlayingDeck(name);
		for (PlayingDeckPlayingCard playingDeckPlayingCard : playingDeckPlayingCards) {
			for (int i = 0; i < playingDeckPlayingCard.getCount(); i++) {
				clone.addOnTop(playingDeckPlayingCard.getPlayingCard().clone());
			}
		}
		clone.shuffle();
		return clone;
	}

	public PlayingCard getCard(long id) {
		PlayingCard card = null;
		for (PlayingCard c : deck) {
			if (c.getIdentifier() == id) {
				card = c;
				break;
			}
		}
		return card;
	}

	public void registerDrawObserver(Player player, Ability ability) {
		List<Ability> observers = drawObservers.get(player);
		if(observers == null){
			observers = new ArrayList<>();
		}
		observers.add(ability);
	}

	public void unRegisterDrawObserver(Player player, Ability ability) {
		drawObservers.get(player).remove(ability);
	}

	public Long getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public void remove(PlayingCard card) {
		deck.remove(card);
		
	}

}
